package com.yonyou.pmclouds.workflow.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.entity.SuperVO;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.exception.IllegalRequestArgException;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.BeanHelper;
import com.yonyou.pmclouds.basecom.util.CommonFieldConst;
import com.yonyou.pmclouds.outter.user.UserInfoQuery;
import com.yonyou.pmclouds.task.rmiitf.TaskRemoteService;
import com.yonyou.pmclouds.team.entity.TeamMemberVO;
import com.yonyou.pmclouds.team.rmiitf.TeamMemberQuery;
import com.yonyou.pmclouds.user.entity.UserInfo;
import com.yonyou.pmclouds.workflow.aspect.PMApprove;
import com.yonyou.pmclouds.workflow.entity.*;
import com.yonyou.pmclouds.workflow.service.ProcessTaskClientService;
import com.yonyou.pmclouds.workflow.service.rmiitf.IWorkFlowCommitService;
import com.yonyou.pmclouds.workflow.service.rmiitf.IWorkFlowService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import yonyou.bpm.rest.request.AssignInfo;
import yonyou.bpm.rest.request.Participant;
import yonyou.bpm.rest.request.task.TaskQueryParam;

import java.util.*;

@Slf4j
@Controller
@RequestMapping(value = {"/mobile/workflow", "/workflow"})
public class WorkFlowController {

    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private TaskRemoteService taskRemoteService;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private IWorkFlowService workFlowService;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private TeamMemberQuery teamMemberQuery;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private IWorkFlowCommitService workFlowCommitService;
    @Autowired
    private UserInfoQuery userInfoQuery;

    @Autowired
    private ProcessTaskClientService processTaskService;

    @ResponseBody
    @RequestMapping(value = "/approve")
    public Object approveInFlow(@RequestBody @Validated ApproveInfoParam approveInfoParam, BindingResult errors) throws BusinessException {
        if (errors.hasErrors()) {
            throw new IllegalRequestArgException();
        }
        RuntimeEnvironment.setTenantId(approveInfoParam.getTenantId());
        //先利用单据类型找到对应的IFlowApproveBuziService的实现类，再利用单据主键查询出单据对象
        SuperVO businessVO = workFlowService.getBuziVOForWorkFlow(approveInfoParam.getPkBill(), approveInfoParam.getBillType());
        /*
         * todo 这里暂时重设tenanid的值 原因：前段传过来的tenantid是错误的，而单据的查询只是是根据pkbill查的。
         *   因此在查询任务的时候会导致 根据错误的租户id和单据pk无法查询到对应的审批任务 zhangwce 20191021
         *   考虑到包还需要上架等流程，因此后端先做调整
         */
        String pkTenant = BeanHelper.getProperty(businessVO,CommonFieldConst.PK_TENANT) == null ?
               String.valueOf(BeanHelper.getProperty(businessVO,CommonFieldConst.PKTENANT) ) : String.valueOf(BeanHelper.getProperty(businessVO,CommonFieldConst.PK_TENANT) );
        RuntimeEnvironment.setTenantId(pkTenant);
        //先找到对应项目里面的组织字段，判断该组织是否为NC的（自己的项目目前没有组织），然后利用租户主键以及NC组织主键查到组织对象，
        //判断该组织是否为启用状态，若组织存在则根据组织主键、租户主键、单据类型查询下面的审批对象。若组织禁用，则查找集团审批流。
        ProcessDefinitionVO definitionVO = processTaskService.getDefinitionVOByType(approveInfoParam.getBillType(), businessVO);

        Object assignInfo = this.assignCheck(approveInfoParam, definitionVO, businessVO);
        if (assignInfo != null)
            return assignInfo;
        workFlowService.approveInFlow(approveInfoParam);

        return "success";
    }

    private Object assignCheck(ApproveInfoParam approveInfoParam, ProcessDefinitionVO definitionVO,
                               SuperVO businessVO) throws BusinessException {
        //获取到该单据的审批任务
        ProcessTaskVO[] processTaskVOs = getLatestBillTask(approveInfoParam.getPkBill(), RuntimeEnvironment.getLoginUserId());
        if (ArrayUtils.isEmpty(processTaskVOs))
            throw new BusinessException("用户没有该单据的审批任务，请刷新后重试");
        //如果该任务不是指派任务且下一环节有指派信息，则返回可指派范围
        if (!approveInfoParam.isAssignFlag()) {
            AssignInfo assignInfo = getNextAssignInfo(processTaskVOs[0].getTaskId(), definitionVO, businessVO);
            if (assignInfo != null) {
                return filterAssignInfoByTeam(definitionVO, businessVO, assignInfo);
            }
        }
        return null;
    }

    /**
     * 根据项目团队过滤标识过滤指派范围
     *
     * @param processDefinitionVO
     * @param origVO
     * @param assignInfo
     * @return
     * @throws BusinessException
     */
    private AssignInfo filterAssignInfoByTeam(ProcessDefinitionVO processDefinitionVO, SuperVO origVO, AssignInfo assignInfo) throws BusinessException {
        // 如果流程定义中，未开启项目团队过滤，则直接返回指派信息
        if (processDefinitionVO.getIsAssignFilter() == 0)
            return assignInfo;

        String pk_project = BeanHelper.getProperty(origVO, CommonFieldConst.PK_PROJECT) == null ?
                (String) BeanHelper.getProperty(origVO, CommonFieldConst.PKPROJECT) :
                (String) BeanHelper.getProperty(origVO, CommonFieldConst.PK_PROJECT);
        // 获得团队中的成员
        TeamMemberVO[] teamMemberVOs = teamMemberQuery.queryByProject(pk_project);
        // 团队成员为空时，只能不根据团队去过滤指派范围
        if (ArrayUtils.isEmpty(teamMemberVOs))
            return assignInfo;
        Participant[] participants = assignInfo.getAssignInfoItems()[0].getParticipants();

        if (ArrayUtils.isEmpty(participants))
            return null;

        List<Participant> teamParticipantList = new ArrayList<Participant>();
        for (Participant participant :
                participants) {
            for (int i = 0; i < teamMemberVOs.length; i++) {
                if (StringUtils.equals(teamMemberVOs[i].getPkUser(), participant.getId()))
                    teamParticipantList.add(participant);
            }
        }

        if (teamParticipantList != null && teamParticipantList.size() > 0)
            assignInfo.getAssignInfoItems()[0].setParticipants(teamParticipantList.toArray(new Participant[0]));

        return assignInfo;
    }

    private ProcessTaskVO[] getLatestBillTask(String pkBill, String pkUser) throws BusinessException {
        TaskQueryParam taskQueryParam = getTaskQueryParam(pkBill, pkUser);
        return processTaskService.queryProcessTaskVOs(taskQueryParam);
    }


    private TaskQueryParam getTaskQueryParam(String pkBill, String pkUser) {
        TaskQueryParam taskQueryParam = new TaskQueryParam();
        taskQueryParam.setProcessInstanceBusinessKey(pkBill);
        taskQueryParam.setAssignee(pkUser);
        return taskQueryParam;
    }

    private AssignInfo getNextAssignInfo(String taskId, ProcessDefinitionVO definitionVO, SuperVO businessVO) throws BusinessException {
        Map<String, Object> checkMap = processTaskService.assignCheck(taskId, definitionVO, businessVO);
        boolean assignAble = (boolean) checkMap.get("assignAble");
        return assignAble ? (AssignInfo) checkMap.get("assignInfo") : null;
    }

    @ResponseBody
    @RequestMapping(value = "/reject")
    public String rejectByFlow(@RequestBody @Validated ApproveInfoParam approveInfoParam, BindingResult errors) throws BusinessException {

        if (errors.hasErrors()) {
            throw new IllegalRequestArgException();
        }

        RuntimeEnvironment.setTenantId(approveInfoParam.getTenantId());

        ProcessTaskVO[] processTaskVOs = getLatestBillTask(approveInfoParam.getPkBill(), RuntimeEnvironment.getLoginUserId());

        if (ArrayUtils.isEmpty(processTaskVOs))
            throw new BusinessException("用户没有该单据的审批任务，请刷新后重试");

        workFlowService.reject(approveInfoParam);

        return "success";
    }

    @RequestMapping("/getApprovalRecord")
    @ResponseBody
    public List<ProcessRunTimeVO> getApprovalRecord(@RequestParam("userId") String userId, @RequestParam("tenantId") String tenantId,
                                                    @RequestParam("businessKey") String businessKey,
                                                    @RequestParam("billType") String billType) throws BusinessException {
        List<ProcessRunTimeVO> runTimeVOList = workFlowService.getApprovalRecord(userId, tenantId, businessKey, billType);
        if (runTimeVOList == null) {
            return null;
        }

        Set<String> userIdSet = new HashSet<>();
        for (ProcessRunTimeVO runTimeVO : runTimeVOList) {
            String allApprovalPersonCode = runTimeVO.getAllApprovalPersonCode();
            if (StringUtils.isNotEmpty(allApprovalPersonCode)) {
                userIdSet.addAll(Arrays.asList(allApprovalPersonCode.split(",")));
            }
        }

        UserInfo[] userInfoArray = userInfoQuery.queryUsers(userIdSet.toArray(new String[]{}));

        Map<String, String> userInfoMap = new HashMap<>();
        if (userInfoArray != null && userInfoArray.length > 0) {
            for (UserInfo userInfo : userInfoArray) {
                userInfoMap.put(userInfo.getUserId(), userInfo.getUserName());
            }

            for (ProcessRunTimeVO runTimeVO : runTimeVOList) {
                StringBuilder stringBuilder = new StringBuilder();
                for (String personCode : runTimeVO.getAllApprovalPersonCode().split(",")) {
                    stringBuilder.append("; ").append(userInfoMap.get(personCode));
                }
                runTimeVO.setAllApprovalPersonName(stringBuilder.substring(1));

                for (ApprovalRecordVO recordVO : runTimeVO.getApprovalRecordVOList()) {
                    recordVO.setApprovalPersonName(userInfoMap.get(recordVO.getApprovalPersonCode()));
                }
            }

        }
        return runTimeVOList;
    }

    @ResponseBody
    @RequestMapping(value = "/checkApprovePerm")
    public boolean checkApprovePerm(String pkBill) throws BusinessException {
        return workFlowService.checkApprovePerm(pkBill, RuntimeEnvironment.getLoginUserId());
    }

    @ResponseBody
    @RequestMapping(value = "/commit")
    @PMApprove(trigger = "commit")
    public Object commit(@RequestBody @Validated CommitFlowParamVO commitFlowParamVO, BindingResult errors) throws BusinessException {
        if (errors.hasErrors()) {
            throw new IllegalRequestArgException();
        }
        return workFlowCommitService.commit(commitFlowParamVO);
    }


}

